﻿#region 참조 네임스페이스

using System;
using System.Collections.Generic;
using System.Threading;

#endregion

namespace Skying.ComponentModel
{
	public static class SynchronizationContextRegister
	{/// <summary>
		/// A mapping from synchronization context type names to their properties. We map from type names instead of actual types to avoid dependencies on unnecessary assemblies.
		/// </summary>
		private static Dictionary<string, SynchronizationContextProperties> synchronizationContextProperties = PredefinedSynchronizationContextProperties();

		/// <summary>
		/// Registers a <see cref="SynchronizationContext"/> type claiming to provide certain guarantees.
		/// </summary>
		/// <param name="synchronizationContextType">The type derived from <see cref="SynchronizationContext"/>.</param>
		/// <param name="properties">The guarantees provided by this type.</param>
		/// <remarks>
		/// <para>This method should be called once for each type of <see cref="SynchronizationContext"/>. It is not necessary to call this method for .NET <see cref="SynchronizationContext"/> types or <see cref="ActionDispatcherSynchronizationContext"/>.</para>
		/// <para>If this method is called more than once for a type, the new value of <paramref name="properties"/> replaces the old value. The flags are not merged.</para>
		/// </remarks>
		public static void Register(Type synchronizationContextType, SynchronizationContextProperties properties)
		{
			lock (synchronizationContextProperties)
			{
				if (synchronizationContextProperties.ContainsKey(synchronizationContextType.FullName))
				{
					synchronizationContextProperties[synchronizationContextType.FullName] = properties;
				}
				else
				{
					synchronizationContextProperties.Add(synchronizationContextType.FullName, properties);
				}
			}
		}

		/// <summary>
		/// Looks up the guarantees for a <see cref="SynchronizationContext"/> type.
		/// </summary>
		/// <param name="synchronizationContextType">The type derived from <see cref="SynchronizationContext"/> to test.</param>
		/// <returns>The properties guaranteed by <paramref name="synchronizationContextType"/>.</returns>
		public static SynchronizationContextProperties Lookup(Type synchronizationContextType)
		{
			lock (synchronizationContextProperties)
			{
				SynchronizationContextProperties supported = SynchronizationContextProperties.None;
				if (synchronizationContextProperties.ContainsKey(synchronizationContextType.FullName))
				{
					supported = synchronizationContextProperties[synchronizationContextType.FullName];
				}

				return supported;
			}
		}

		/// <summary>
		/// Verifies that a <see cref="SynchronizationContext"/> satisfies the guarantees required by the calling code.
		/// </summary>
		/// <param name="synchronizationContextType">The type derived from <see cref="SynchronizationContext"/> to test.</param>
		/// <param name="properties">The guarantees required by the calling code.</param>
		public static void Verify(Type synchronizationContextType, SynchronizationContextProperties properties)
		{
			SynchronizationContextProperties supported = Lookup(synchronizationContextType);
			if ((supported & properties) != properties)
			{
				throw new InvalidOperationException("This asynchronous object cannot be used with this SynchronizationContext");
			}
		}

		/// <summary>
		/// Verifies that <see cref="SynchronizationContext.Current"/> satisfies the guarantees required by the calling code.
		/// </summary>
		/// <param name="properties">The guarantees required by the calling code.</param>
		public static void Verify(SynchronizationContextProperties properties)
		{
			if (SynchronizationContext.Current == null)
			{
				Verify(typeof(SynchronizationContext), properties);
			}
			else
			{
				Verify(SynchronizationContext.Current.GetType(), properties);
			}
		}

		/// <summary>
		/// Returns the mapping for all predefined (.NET) <see cref="SynchronizationContext"/> types.
		/// </summary>
		/// <returns>The mapping for all predefined (.NET) <see cref="SynchronizationContext"/> types.</returns>
		private static Dictionary<string, SynchronizationContextProperties> PredefinedSynchronizationContextProperties()
		{
			Dictionary<string, SynchronizationContextProperties> ret = new Dictionary<string, SynchronizationContextProperties>();
			ret.Add("System.Threading.SynchronizationContext", SynchronizationContextProperties.NonReentrantPost);
			ret.Add("System.Windows.Forms.WindowsFormsSynchronizationContext", SynchronizationContextProperties.Standard);
			ret.Add("System.Windows.Threading.DispatcherSynchronizationContext", SynchronizationContextProperties.Standard);

			// AspNetSynchronizationContext does not provide any guarantees at all, so it is not added here
			return ret;
		}
	}
}
